package com.shadowlight.hero_story;

import com.google.protobuf.GeneratedMessageV3;
import com.shadowlight.hero_story.cmdHandler.HandlerFactory;
import com.shadowlight.hero_story.cmdHandler.ICmdHandler;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.log4j.Log4j2;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Description 主线程处理器，防止多线程导致的不一致问题，业务代码都放到主线程处理器里面执行
 * 1）不一致问题：如果两个用户在不同的线程里面攻击第三个用户，会导致两次攻击只算一次的情况
 * 2）主线程处理器使用单线程来处理，避免多线程造成的数据不一致问题
 * @Author cz
 * @Date 2022/3/14
 */
@Log4j2
public final class MainThreadProcessor {


    private static final MainThreadProcessor MAIN_THREAD_PROCESSOR = new MainThreadProcessor();
    private static final ExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadExecutor(r -> {
        Thread thread = new Thread(r);
        thread.setName("MainThreadProcessor");
        return thread;
    });


    private MainThreadProcessor() {
    }


    public static MainThreadProcessor getInstance() {
        return MAIN_THREAD_PROCESSOR;
    }

    public void process(ChannelHandlerContext cx, Object msg) {
        if (cx == null || msg == null) {
            return;
        }

        EXECUTOR_SERVICE.submit(() -> {
            ICmdHandler<? extends GeneratedMessageV3> cmdHandler = HandlerFactory.create(msg.getClass());

            if (cmdHandler != null) {
                try {
                    cmdHandler.handle(cx, cast(msg));
                } catch (Exception e) {
                    log.error("单线程处理出错:{}", e.getMessage(), e);
                }
            }
        });
    }

    public void process(Runnable r) {
        EXECUTOR_SERVICE.submit(new SafeRun(r));
    }


    private static <TCmd extends GeneratedMessageV3> TCmd cast(Object msg) {
        if (msg == null) {
            return null;
        } else {
            return (TCmd) msg;
        }
    }

    public static class SafeRun implements Runnable {
        Runnable r;

        public SafeRun(Runnable r) {
            this.r = r;
        }

        @Override
        public void run() {
            try {
                r.run();
            } catch (Exception e) {
                log.error("主线程里面执行出错了！");
            }
        }
    }
}
